#ifdef _WIN32

#include "Filesystem.h"
#include "Path.h"
#include "Exceptions.win32.h"
#include "Format.h"
#include <Windows.h>
#include <boost/scope_exit.hpp>

using namespace std;

namespace lhcutils
{

FileSysEntityType::Value GetFileSysEntityType(const UnicodeString& filePath, SymlinkBehavior::Value symlinkBehavior)
{
	boost::shared_ptr<const CountedArray<wchar_t> > winFilePath = filePath.ToWinStrNullTerminated();
	DWORD attributes = GetFileAttributesW(winFilePath->Ptr);
	if(attributes == INVALID_FILE_ATTRIBUTES)
	{
		THROW_WIN32_DERIVED_IO(Format(*ustr("Could not determine what type of file {0} is."), filePath));
	}

	if(attributes & FILE_ATTRIBUTE_REPARSE_POINT == FILE_ATTRIBUTE_REPARSE_POINT)
	{
		if(symlinkBehavior == SymlinkBehavior::DontFollowLinks)
		{
			return FileSysEntityType::Symlink;
		}
		else
		{
			// If we need to follow symlinks, get the file system to do it for us.
			// open file ultimately pointed to with CreateFile
			// Use GetFileInformationByHandle to get information that way
			HANDLE linkHandle = CreateFile(winFilePath->Ptr, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
			if(linkHandle == INVALID_HANDLE_VALUE)
			{
				THROW_WIN32_DERIVED_IO(Format(*ustr("Could not determine what type of file {0} is."), filePath));
			}

			BOOST_SCOPE_EXIT( (&linkHandle) )
			{
				UNUSED BOOL success = CloseHandle(linkHandle);
			} BOOST_SCOPE_EXIT_END

			BY_HANDLE_FILE_INFORMATION linkInformation;
			BOOL success = GetFileInformationByHandle(linkHandle, &linkInformation);
			if(success == 0)
			{
				THROW_WIN32IO(Format(*ustr("Could not determine what type of file {0} is."), filePath));
			}

			if(linkInformation.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT == FILE_ATTRIBUTE_REPARSE_POINT)
			{
				return FileSysEntityType::Symlink;
			}
			else if(attributes & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY)
			{
				return FileSysEntityType::Directory;
			}
			else
			{
				return FileSysEntityType::File;
			}
		}
	}
	else if(attributes & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY)
	{
		return FileSysEntityType::Directory;
	}
	else
	{
		return FileSysEntityType::File;
	}
}

vector<StringPtr> GetEntitiesInDirectory(const UnicodeString& dirPath)
{
	const UnicodeString searchString = dirPath + *ustr("\\*");
	
	boost::shared_ptr<const CountedArray<wchar_t> > winSearchString = searchString.ToWinStrNullTerminated();
	WIN32_FIND_DATAW findData;

	HANDLE listHandle = FindFirstFileW(winSearchString->Ptr, &findData);
	if(listHandle == INVALID_HANDLE_VALUE)
	{
		THROW_WIN32_DERIVED_IO(Format(*ustr("Could not get a list of the files in the directory {0}."), dirPath));
	}

	BOOST_SCOPE_EXIT( (&listHandle) )
	{
		UNUSED BOOL success = FindClose(listHandle);
	} BOOST_SCOPE_EXIT_END

	vector<StringPtr> entityPaths;
		
	BOOL success = 1;
	do
	{
		StringPtr entityFileName = winstr(findData.cFileName);
		if(entityFileName != ustr(".") && entityFileName != ustr(".."))
		{
			StringPtr entityPath = Path::Combine(dirPath, *entityFileName);
			entityPaths.push_back(entityPath);
		}

		success = FindNextFileW(listHandle, &findData);
	} while(success != 0);

	DWORD errorCode = GetLastError();
	if(errorCode != ERROR_NO_MORE_FILES)
	{
		THROW_WIN32IO(Format(*ustr("Could not get a list of the files in the directory {0}."), dirPath));
	}

	return entityPaths;
}

}

#endif

/*
 Copyright 2011 Greg Najda

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
